package notify

import (
	"context"
	"crypto/sha1"
	"encoding/hex"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"runtime"
	"sort"
	"strings"
	"time"

	V1 "open/api/open/service/v1"
	"open/internal/model"
	"open/internal/model/field"

	apiErr "github.com/go-kratos/kratos/v2/errors"
	"gorm.io/gorm"

	v1 "open/api/open/notify/v1"
	"open/internal/model/event"
	"open/internal/types"
	"open/pkg/log"
	wechatCrypto "gitee.com/quantum-force/wxopen-api/crypto"
)

// 事件列表
const (
	componentVerifyTicket = "component_verify_ticket"
	unauthorized          = "unauthorized"
	updateAuthorized      = "updateauthorized"
	authorized            = "authorized"
)

// NotifyWechatOpenAppAuthorize 微信开放平台授权
func (s *Service) NotifyWechatOpenAppAuthorize(ctx context.Context, req *v1.NotifyWechatOpenAppAuthorizeRequest) (*v1.NotifyWechatOpenAppAuthorizeReply, error) {
	openApp, err := s.dao.GetWechatOpenApp(ctx, req.ComponentAppId)
	if err != nil {
		return nil, err
	}
	if req.Encrypt != "" {
		rawXML, err := wechatCrypto.DecryptMessage(req.AppId, req.Encrypt, openApp.Token, openApp.ASEKey, req.MsgSignature, req.Timestamp, req.Nonce)
		if err != nil {
			return nil, apiErr.BadRequest("DECRYPT_ERROR", "解析错误")
		}
		if err = xml.Unmarshal(rawXML, &req); err != nil {
			return nil, apiErr.BadRequest("DECRYPT_ERROR", "解析错误")
		}
	}
	// log.FromContext(ctx).Infof("接收到授权事件推送%s", req.InfoType)
	log.FromContext(ctx).Infof("接收到授权事件推送%+v", req)
	switch req.InfoType {
	case componentVerifyTicket:
		openApp.Ticket = req.ComponentVerifyTicket
		if err := s.dao.SaveWechatOpenApp(ctx, openApp); err != nil {
			return nil, err
		}
	case unauthorized:
		subApp, err := s.dao.GetWechatSubApp(ctx, req.AuthorizerAppid)
		if err != nil {
			return nil, err
		}

		subApp.Status = types.WechatUnauthorized
		subApp.RefreshToken = ""
		subApp.Token = ""
		subApp.ExpiredAt = nil
		err = s.dao.SaveWechatSubApp(ctx, subApp)
		if err != nil {
			return nil, err
		}
		return &v1.NotifyWechatOpenAppAuthorizeReply{Message: "success"}, nil
	case updateAuthorized, authorized:
		authInfo, err := s.dao.AuthorizeWechat(ctx, req.AuthorizationCode)
		if err != nil {
			return nil, err
		}
		if err := s.dao.Transaction(ctx, func(ctx context.Context) error {
			subApp, err := s.dao.LockWechatAuthApp(ctx, authInfo.AppID)
			if err != nil {
				// 授权通知比前端调用先到
				if errors.Is(err, gorm.ErrRecordNotFound) {
					subApp = &model.WechatOpenSubApp{
						OpenAppID:    req.ComponentAppId,
						AppID:        authInfo.AppID,
						Token:        authInfo.Token,
						RefreshToken: authInfo.RefreshToken,
						ExpiredAt:    &authInfo.ExpiredAt,
						Status:       types.WechatAuthorized,
					}
					if err = s.dao.CreateWechatSubApp(ctx, subApp); err != nil {
						return err
					}
				}
			} else {
				subApp.Token = authInfo.Token
				subApp.Status = types.WechatAuthorized
				subApp.RefreshToken = authInfo.RefreshToken
				subApp.ExpiredAt = &authInfo.ExpiredAt
				if err := s.dao.SaveWechatSubApp(ctx, subApp); err != nil {
					return err
				}
			}
			if err != nil {
				return err
			}

			schedule := model.Schedule{
				RefID:     subApp.ID,
				JobType:   types.TaskSyncWechatPubTemplate,
				JobData:   field.JSON("{}"),
				JobStatus: types.ScheduleTaskInit,
			}
			if err := s.dao.CreateSchedule(ctx, &schedule); err != nil {
				return err
			}
			if err := s.dao.TxSend(ctx, &event.TaskSyncWechatPubTemplate{JobID: schedule.ID, AppID: authInfo.AppID}); err != nil {
				return err
			}
			return nil
		}); err != nil {
			return nil, err
		}

		return &v1.NotifyWechatOpenAppAuthorizeReply{Message: "success"}, nil
	default:
		return nil, fmt.Errorf("微信通知了错误的通知类型： %s", req.InfoType)
	}
	return &v1.NotifyWechatOpenAppAuthorizeReply{Message: "success"}, nil
}

// NotifyWechatOpenApp 处理微信通知事件
func (s *Service) NotifyWechatOpenApp(ctx context.Context, req *v1.NotifyWechatOpenAppRequest) (*v1.NotifyWechatOpenAppReply, error) {
	// 1. 查询开放应用
	openApp, err := s.dao.GetWechatOpenApp(ctx, req.ComponentAppId)
	if err != nil {
		return nil, err
	}
	// 2. 应用解密消息
	if req.Encrypt != "" {
		rawXML, err := wechatCrypto.DecryptMessage(req.ComponentAppId,
			req.Encrypt,
			openApp.Token,
			openApp.ASEKey,
			req.MsgSignature,
			req.Timestamp,
			req.Nonce)
		if err != nil {
			return nil, apiErr.BadRequest("DECRYPT_ERROR", "解析错误")
		}
		if err = xml.Unmarshal(rawXML, &req); err != nil {
			return nil, apiErr.BadRequest("DECRYPT_ERROR", "解析错误")
		}
	}
	// 微信全网发布自动化测试相关
	//switch req.NotifyAppId {
	////case "wx570bc396a51b8ff8", "wxd101a85aa106f53e",
	////	"wx9252c5e0bb1836fc", "wxc39235c15087f6f3",
	////	"wx8e1097c5bc82cde9", "wx7720d01d4b2a4500",
	////	"wx14550af28c71a144", "wx05d483572dcd5d8b",
	////	"wxa35b9c23cfe664eb", "wx5910277cae6fd970":
	switch req.MsgType {
	case "text":
		log.FromContext(ctx).Infof("接收到消息推送: text 内容:%s", req.Content)
		if req.Content == "TESTCOMPONENT_MSG_TYPE_TEXT" {
			reMsg := fmt.Sprintf(`<xml><ToUserName><![CDATA[%s]]></ToUserName>
	               <FromUserName><![CDATA[%s]]></FromUserName>
	               <CreateTime>%s</CreateTime><MsgType><![CDATA[text]]></MsgType>
	               <Content><![CDATA[TESTCOMPONENT_MSG_TYPE_TEXT_callback]]></Content>
	               </xml>`, req.FromUserName, req.ToUserName, req.Timestamp)
			xmlString, err := wechatCrypto.EncryptMsg(req.ComponentAppId, reMsg, openApp.Token, openApp.ASEKey, req.Timestamp, req.Nonce)
			if err != nil {
				return nil, err
			}
			return &v1.NotifyWechatOpenAppReply{Message: xmlString}, nil
		}
		logger := log.FromContext(ctx)
		if strings.Contains(req.Content, "QUERY_AUTH_CODE") {
			go func() {
				defer func() {
					if rErr := recover(); rErr != nil {
						buf := make([]byte, 64<<10)
						n := runtime.Stack(buf, false)
						buf = buf[:n]
						log.FromContext(ctx).Errorf("%v: %+v\n%s\n", rErr, req, buf)
					}
				}()

				newCtx := context.Background()
				newCtx = log.WithLogger(newCtx, logger)
				msgCmd := &V1.SendWechatCustomMessageRequest{
					AppId:   req.NotifyAppId,
					ToUser:  req.FromUserName,
					MsgType: "text",
					Text:    &V1.Text{Content: fmt.Sprintf("%s_from_api", strings.Split(req.Content, "QUERY_AUTH_CODE:")[1])},
				}
				token, err := s.dao.GetWechatAuthorizerAccessToken(newCtx, req.NotifyAppId)
				if err != nil {
					return
				}
				err = s.dao.SendWechatCustomMessage(newCtx, token, msgCmd)
				if err != nil {
					logger.Error("回复QUERY_AUTH_CODE错误", err)
					return
				}
			}()
			return &v1.NotifyWechatOpenAppReply{Message: "success"}, nil
		}

	case "event":
		switch req.Event {
		case "weapp_audit_success":
			err := s.dao.Transaction(ctx, func(ctx context.Context) error {
				appVersion, err := s.dao.LockAppVersion(ctx, req.NotifyAppId, types.Wechat)
				if err != nil {
					return err
				}
				appVersion.Stage = types.StageWaitRelease
				if err = s.dao.SaveAppVersion(ctx, appVersion); err != nil {
					return err
				}
				schedule := model.Schedule{
					RefID:     appVersion.ID,
					JobType:   types.TaskRelease,
					JobData:   field.JSON("{}"),
					JobStatus: types.ScheduleTaskInit,
				}
				if err = s.dao.CreateSchedule(ctx, &schedule); err != nil {
					return err
				}
				if err = s.dao.TxSend(ctx, &event.TaskRelease{
					AppID:     req.NotifyAppId,
					Platform:  types.Wechat,
					JobID:     schedule.ID,
					Timestamp: time.Now().Unix(),
				}); err != nil {
					return err
				}
				return nil
			})
			if err != nil {
				return nil, err
			}
		case "weapp_audit_fail":
			err := s.dao.Transaction(ctx, func(ctx context.Context) error {
				appVersion, err := s.dao.LockAppVersion(ctx, req.NotifyAppId, types.Wechat)
				if err != nil {
					return err
				}
				if appVersion.Stage != types.StageAuditing {
					return nil
				}
				appVersion.Stage = types.StageAuditReject
				appVersion.Status = types.StatusReject
				appVersion.RejectReason = req.Reason
				if err = s.dao.SaveAppVersion(ctx, appVersion); err != nil {
					return err
				}
				return nil
			})
			if err != nil {
				return nil, err
			}
		}
		return &v1.NotifyWechatOpenAppReply{Message: "success"}, nil
	}
	return &v1.NotifyWechatOpenAppReply{Message: "success"}, nil
	//}
	//return &v1.NotifyWechatOpenAppReply{Message: "success"}, nil
}

// CheckMP 检查公众号
func (s *Service) CheckMP(ctx context.Context, req *v1.CheckMPRequest) (*v1.CheckMPReply, error) {
	app, err := s.dao.GetWechatMPApp(ctx, req.AppId)
	if err != nil {
		return nil, err
	}
	ss := []string{app.Token, req.Timestamp, req.Nonce}
	sort.Strings(ss)
	h := sha1.New()
	_, _ = io.WriteString(h, strings.Join(ss, ""))
	if hex.EncodeToString(h.Sum(nil)) != req.Signature {
		return nil, fmt.Errorf("公众号校验错误")
	}
	return &v1.CheckMPReply{Message: req.Echostr}, nil
}

// NotifyWechatMPApp 处理微信公众号通知事件
func (s *Service) NotifyWechatMPApp(ctx context.Context, req *v1.NotifyWechatMPAppRequest) (*v1.NotifyWechatMPAppReply, error) {
	// 1. 查询公众号应用
	app, err := s.dao.GetWechatMPApp(ctx, req.AppId)
	if err != nil {
		return nil, err
	}
	// 2. 应用解密消息
	if req.Encrypt != "" {
		rawXML, err := wechatCrypto.DecryptMessage(app.AppID,
			req.Encrypt,
			app.Token,
			app.ASEKey,
			req.MsgSignature,
			req.Timestamp,
			req.Nonce)
		if err != nil {
			return nil, apiErr.BadRequest("DECRYPT_ERROR", "解析错误")
		}
		if err = xml.Unmarshal(rawXML, &req); err != nil {
			return nil, apiErr.BadRequest("DECRYPT_ERROR", "解析错误")
		}
	}
	// 3. 获取公众号AccessToken
	token, err := s.dao.GetWechatMPAccessToken(ctx, req.AppId)
	if err != nil {
		return nil, err
	}
	// 4. 处理消息
	if req.MsgType == "event" && req.Ticket != "" {
		// 4.1查询用户信息
		user, err := s.dao.GetWechatUserInfo(ctx, token, req.FromUserName)
		if err != nil {
			return nil, err
		}
		subscribeEvent := event.NewTicketSubscribedEvent(
			req.Ticket,
			user.OpenID,
			user.UnionID,
			user.Nickname,
			user.Avatar)
		// 3.2发送事件
		err = s.dao.Send(ctx, subscribeEvent)
		if err != nil {
			return nil, err
		}

	}
	// 4. 返回DTO
	return &v1.NotifyWechatMPAppReply{Message: "success"}, nil
}
